/////////////////////////////////////////////////////////////////////////////
// Purpose:     Log management
// Author:      Jesus Gonzalez
// Modified by:
// Copyright:   (c) 2003-2013 Jesus Gonzalez <jgonzalez@gdr-sistemas.com>
// License:     BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _SMARTLIB_LOG_H_
#define _SMARTLIB_LOG_H_

/**
 * \file
 * %Log management.
 */

///@defgroup log Logging
///@{

#ifndef LOG_CATEGORY
/**
 * Name of the category for the messages that will be logged.
 *
 * By default, logged messages will be recorded using the default (empty) category.
 *
 * To set a custom category just define the macro with the name of the category
 * before including <SmartLib/Log.h>.
 *
 * @par Example
 * @code{.cpp}
 * #define LOG_CATEGORY "MyCategoryName"
 * #include <SmartLib/Log.h>
 * @endcode
 */
#define LOG_CATEGORY ((const char*)0)
#endif

/**
 * @def LOG_PRIORITY_MAX
 *
 * Sets at compile-time the lowest priority limit for logging messages.
 *
 * Messages with priority lower (higher "value") than LOG_PRIORITY_MAX priority will be discarded
 * at compile-time.
 *
 * By default, the compile-time lowest priority will be LOG_PRIORITY_INFO for "Release"
 * configurations and LOG_PRIORITY_TRACE for "Debug" configurations.
 *
 * To set a custom lowest priority, define the macro with the numeric value corresponding to the
 * custom priority before including <SmartLib/Log.h> or in compiler command-line parameters
 * (e.g. \p -DLOG_PRIORITY_MAX=n).
 *
 * @remark
 * At runtime, calling Log::setPriorityLimit() can further
 *
 * The macro LOG_PRIORITY_MAX sets the lowest priority limit at compile-time. Therefore,
 * setting the log priority at runtime lower than LOG_PRIORITY_MAX will behave just as
 * setting it to LOG_PRIORITY_MAX.
 *
 * @see Log::setPriorityLimit()
 */

///@name Log Priority Values
///@{
#define LOG_PRIORITY_ERROR			1 ///< %Log priority \p ERROR numeric value
#define LOG_PRIORITY_WARN			2 ///< %Log priority \p WARN numeric value
#define LOG_PRIORITY_INFO			3 ///< %Log priority \p INFO numeric value
#define LOG_PRIORITY_DEBUG			4 ///< %Log priority \p TRACE numeric value
#define LOG_PRIORITY_TRACE          5 ///< %Log priority \p DEBUG numeric value
#define LOG_PRIORITY_DEBUG_EXTRA	6 ///< %Log priority \p DEBUG_EXTRA numeric value
#define LOG_PRIORITY_ALLOC			7 ///< %Log priority \p ALLOC numeric value
///@}

#ifndef LOG_PRIORITY_MAX
#ifdef _DEBUG_
#define LOG_PRIORITY_MAX                LOG_PRIORITY_TRACE
#else
#define LOG_PRIORITY_MAX                LOG_PRIORITY_INFO
#endif
#endif

///@cond
#ifdef __GNUC__
#define __FUNCTION_INFO__ 	__PRETTY_FUNCTION__
#else
#define __FUNCTION_INFO__ 	__FUNCTION__
#endif
///@endcond

#include <stdexcept>
#include <SmartLib/FormatString.h>
#include <SmartLib/Exceptions.h>
#include <SmartLib/SmartPtr.h>

/**
 * The LogHandler abstract class is the base class for implementations of log handlers
 * (to process the log messages generated by the application).
 */
class LogHandler : public SmartObject
{
public:
    DECLARE_SMARTPTR(LogHandler)

    /**
     * This method is called by the log management system when a log message is generated by the application.
     *
     * @param[in] prio Priority of the message
     * @param[in] category Category of the message
     * @param[in] function Name of the function or method where the message was generated
     * @param[in] msg Message text
     * @return \p True if the message must be also logged to console (if verbose mode is activated),
     *         \p false otherwise
     */
	virtual bool Process( int prio, const char* category, const char* function, const char* msg ) = 0;
};

/**
 * The Log singleton class provides access to the log management functionalities.
 */
class Log
{
public:
    ///@cond INTERNAL
    /**
     * Logs a message.
     *
     * @param[in] prio Priority of the message (one of LOG_PRIORITY_xxx)
     * @param[in] category Category of the message (may be NULL)
     * @param[in] function Name of the function or method where the message was generated
     * @param[in] msg Message format string (using printf format)
     * @param[in] ... Variable parameters for the format string
     */
    static void LogMessage( int prio, const char* category, const char* function, const char* msg, ... );
    ///@endcond

    /**
     * Gets the current priority limit for logging messages.
     */
    static int getPriorityLimit()
    {
        return g_priorityLimit;
    }

    /**
     * Sets the runtime priority limit for logging messages.
     *
     * Messages with priority lower (higher "value") than the given priority will be ignored.
     *
     * @remark
     * The macro LOG_PRIORITY_MAX sets the lowest priority limit at compile-time. Therefore,
     * setting the log priority at runtime lower than LOG_PRIORITY_MAX will behave just as
     * setting it to LOG_PRIORITY_MAX.
     *
     * @see LOG_PRIORITY_MAX
     */
    static void setPriorityLimit(int logPriorityLimit)
    {
        g_priorityLimit = logPriorityLimit;
    }

    /**
     * Gets the current installed log handler.
     */
    static const LogHandler::Ptr& getLogHandler()
    {
        return g_logHandler;
    }

    /**
     * Sets a new log handler.
     */
    static void setLogHandler(const LogHandler::Ptr& userLogHandler)
    {
        g_logHandler = userLogHandler;
    }

private:
    Log() {}; // Make it abstract

    static int g_priorityLimit;
    static LogHandler::Ptr g_logHandler;
};

///@name Message Logging Macros
///@{

///@cond INTERNAL
/**
 * Logs a formatted message with the given priority.
 */
#define LOG(prio,str,...) Log::LogMessage( prio, LOG_CATEGORY, __FUNCTION_INFO__, str, ##__VA_ARGS__ )
///@endcond

/**
 * \def LOG_ERROR
 * Logs a message with \p ERROR priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_ERROR
#define LOG_ERROR(str,...) LOG( LOG_PRIORITY_ERROR, str, ##__VA_ARGS__ )
#else
#define LOG_ERROR(str,...)
#endif

/**
 * \def LOG_WARN
 * Logs a message with \p WARN priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_WARN
#define LOG_WARN(str,...) LOG( LOG_PRIORITY_WARN, str, ##__VA_ARGS__ )
#else
#define LOG_WARN(str,...)
#endif

/**
 * \def LOG_INFO
 * Logs a message with \p INFO priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_INFO
#define LOG_INFO(str,...) LOG( LOG_PRIORITY_INFO, str, ##__VA_ARGS__ )
#else
#define LOG_INFO(str,...)
#endif

/**
 * \def LOG_TRACE
 * Logs a message with \p TRACE priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_TRACE
#define LOG_TRACE(str,...) LOG( LOG_PRIORITY_TRACE, str, ##__VA_ARGS__ )
#else
#define LOG_TRACE(str,...)
#endif

/**
 * \def LOG_DEBUG
 * Logs a message with \p DEBUG priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_DEBUG
#define LOG_DEBUG(str,...) LOG( LOG_PRIORITY_DEBUG, str, ##__VA_ARGS__ )
#else
#define LOG_DEBUG(str,...)
#endif

/**
 * \def LOG_DEBUG_EXTRA
 * Logs a message with \p DEBUG_EXTRA priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_DEBUG_EXTRA
#define LOG_DEBUG_EXTRA(str,...) LOG( LOG_PRIORITY_DEBUG_EXTRA, str, ##__VA_ARGS__ )
#else
#define LOG_DEBUG_EXTRA(str,...)
#endif

/**
 * \def LOG_ALLOC
 * Logs a message with \p ALLOC priority.
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#if LOG_PRIORITY_MAX >= LOG_PRIORITY_ALLOC
#define LOG_ALLOC(str,...)	LOG( LOG_PRIORITY_ALLOC, str, ##__VA_ARGS__ )
#else
#define LOG_ALLOC(str,...)
#endif

///@}

///@name Utility Macros
///@{

/**
 * Useful macro to trace function/method entries.
 *
 * @par Example
 * @code{.cpp}
 * void function(int p)
 * {
 *     TRACE_FUNCTION_ENTRY
 *
 *     // ... Do something ...
 * }
 * @endcode
 */
#define TRACE_FUNCTION_ENTRY  LOG_TRACE("Entry");

///@}

///@name Exception Throwing Macros
///@{

///@cond INTERNAL
#define THROW_EXCEPTION(EXCLS) \
	throw EXCLS( LOG_CATEGORY, __FUNCTION_INFO__, false )

#define THROW_STR_EXCEPTION(EXCLS, str,...) \
	throw EXCLS( LOG_CATEGORY, __FUNCTION_INFO__, FormatString(str, ##__VA_ARGS__), false )

#if LOG_PRIORITY_MAX >= LOG_PRIORITY_ERROR

#define THROW_EXCEPTION_LOG(EXCLS) \
	throw EXCLS( LOG_CATEGORY, __FUNCTION_INFO__, true )

#define THROW_STR_EXCEPTION_LOG(EXCLS, str,...) \
	throw EXCLS( LOG_CATEGORY, __FUNCTION_INFO__, FormatString(str, ##__VA_ARGS__), true )

#else

#define THROW_EXCEPTION_LOG(EXCLS) \
	THROW_EXCEPTION( EXCLS )

#define THROW_STR_EXCEPTION_LOG(EXCLS, str,...) \
	THROW_STR_EXCEPTION( EXCLS, str, ##__VA_ARGS__ )

#endif
///@endcond INTERNAL

/**
 * Throws a RuntimeError exception with a custom error message.
 *
 * The error message won't be logged unless the exception is not caught or
 * if it's explicitly logged (calling RuntimeError::Log()).
 *
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#define THROW_ERROR(str,...) \
		THROW_STR_EXCEPTION( RuntimeError, str, ##__VA_ARGS__ )

/**
 * Throws a RuntimeError exception with a custom error message, which is immediately
 * logged.
 *
 * @param[in] str Message format string (using printf format)
 * @param[in] ... Variable parameters for the format string
 */
#define THROW_ERROR_LOG(str,...) \
		THROW_STR_EXCEPTION_LOG( RuntimeError, str, ##__VA_ARGS__ )

///@}

///@}

#endif // _SMARTLIB_LOG_H_
